package com.common.core.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 
 * 通用的拷贝工具类<br>
 *
 * @author:   zengjianwen
 * Date:   		2016年1月12日 下午3:20:05
 */
public class CopyUtil {
    protected final static Logger LOGGER = LoggerFactory.getLogger(CopyUtil.class);

    private CopyUtil() {

    }

    @SuppressWarnings("serial")
    private final static List<Class<?>> PrimitiveClasses = new ArrayList<Class<?>>() {
        {
            add(Long.class);
            add(Double.class);
            add(Integer.class);
            add(String.class);
            add(Boolean.class);
            add(java.util.Date.class);
            add(java.sql.Date.class);
        }
    };

    private final static boolean _IsPrimitive(Class<?> cls) {
        return cls.isPrimitive() || PrimitiveClasses.contains(cls);
    }

    /**
     * 
     * 功能描述: 拷贝对象的属性<br>
     * 〈功能详细描述〉若对象中含有集合，fromObj与toObjClazz中的集合存储的对象的属性类型一样,也能实现拷贝 如：MdseInfoVo mdseInfoVo =
     * CopyUtil.copyObject(mdseDocument, MdseInfoVo.class);
     *
     * @param fromObj
     * @param toObjClazz
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    @SuppressWarnings("unchecked")
    public static <T> T copyObject(Object fromObj, Class<T> toObjClazz) {
        try {

            Class<?> fromObjClazz = fromObj.getClass();
            // 普通类型直接返回
            if (_IsPrimitive(toObjClazz))
                return (T) fromObj;

            T toObj = toObjClazz.newInstance();

            // 获取实体类的所有属性，返回Field数组
            Field[] toFields = toObjClazz.getDeclaredFields();
            for (Field toF : toFields) {
                try {
                    //如果当前字段不copy直接跳过
                    NotCopy annotation = toF.getAnnotation(NotCopy.class);
                    if(annotation != null){
                        continue;
                    }

                    int mod = toF.getModifiers();
                    // 静态成员及常量成员不copy
                    if (Modifier.isFinal(mod) || Modifier.isStatic(mod)) {
                        continue;
                    }

                    String toFieldName = toF.getName();
                    String fromFieldName;
                    Mapping mapping = toF.getAnnotation(Mapping.class);

                    if (mapping == null || mapping.name() == null || "".equals(mapping.name().trim())) {
                        fromFieldName = toFieldName;
                    } else {
                        fromFieldName = mapping.name();
                    }

                    toF.setAccessible(true);
                    Field fromF = fromObjClazz.getDeclaredField(fromFieldName);
                    fromF.setAccessible(true);
                    toF.set(toObj, fromF.get(fromObj));
                } catch (Exception e) {
                    if (e instanceof IllegalArgumentException)
                        LOGGER.error("in CopyUtil#copyObject IllegalArgumentException:{}", e);
                }
            }
            return toObj;
        } catch (Exception e) {
            LOGGER.error("in CopyUtil#copyObject exception:{}", e);
        }
        return null;

    }

    /**
     * copy list对象
     * 
     * @param fromObjList
     * @param toObjClazz
     * @return
     */
    public static <T> List<T> copyList(List<?> fromObjList, Class<T> toObjClazz) {
        List<T> toObjList = new ArrayList<T>(fromObjList.size());

        for (int i = 0; i < fromObjList.size(); i++) {
            toObjList.add(copyObject(fromObjList.get(i), toObjClazz));
        }
        return toObjList;
    }

    /**
     * copy list对象
     *
     * @param fromObjList
     * @param toObjClazz
     * @return
     */
    public static <T> Set<T> copySet(Set<?> fromObjList, Class<T> toObjClazz) {
        Set<T> toObjSet = fromObjList.stream().map(ob -> {
            return copyObject(ob, toObjClazz);
        }).collect(Collectors.toSet());
        return toObjSet;
    }

    /**
     * copy map 对象
     * 
     * @param fromObjMap
     * @param toObjClazz
     * @return
     */
    public static <T> Map<String, T> copyMap(Map<String, ?> fromObjMap, Class<T> toObjClazz) {
        Map<String, T> toObjMap = new HashMap<String, T>(fromObjMap.size());
        Iterator<String> iter = fromObjMap.keySet().iterator();
        while (iter.hasNext()) {
            String key = iter.next();
            Object fromObj = fromObjMap.get(key);

            // if(List.class.isAssignableFrom(fromObj.getClass())){
            // toObjMap.put(key, copyList((List<?>)fromObj, toObjClazz));
            // }

            toObjMap.put(key, copyObject(fromObj, toObjClazz));
        }
        return toObjMap;
    }

    public static <T> List<T> copyListMap(List<Map<String, ?>> mapList, Class<T> toObjClass) {
        List<T> toObjList = new ArrayList<T>(mapList.size());
        for (Map<String, ?> map : mapList) {
            toObjList.add(copyMapToBean(map, toObjClass));
        }
        return toObjList;
    }

    public static <T> T copyMapToBean(Map<String, ?> map, Class<T> toObjClass) {
        try {
            Set<String> set = map.keySet();
            T objT = toObjClass.newInstance();
            for (String key : set) {
                try {
                    Object value = map.get(key);
                    Field toF = toObjClass.getDeclaredField(key);
                    toF.setAccessible(true);
                    toF.set(objT, value);
                } catch (Exception e) {
                    LOGGER.error("in CopyUtil#copyMapToBean exception:{}", e);
                }
            }
            return objT;
        } catch (Exception e) {
            LOGGER.error("in CopyUtil#copyMapToBean exception:{}", e);
        }
        return null;
    }
}
